﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using VDataAdapter.WorkerManagement;
using VDataAdapter.BlockingQueue;
using VDataAdapter.PriceServerAdapter;
using log4net;

namespace VDataAdapter.ClientManagement
{
    class ClientManager
    {
        protected readonly ILog _log = LogManager.GetLogger(typeof(ClientManager));

        private IWorkerScheduler _workerScheduler;
        private IPAddress _ipAddress;
        private int _tcpPort;
        private TcpListener _serverSocket;
        private Thread _listenThread;
        private List<Client> _clientList;
        private IPSEventQueueEnqueuerFactory _psEventHandlerFactory;

        public ClientManager(IPAddress ipAddress, int port, IWorkerScheduler workerScheduler, int maxNoClient, IPSEventQueueEnqueuerFactory psEventHandlerFactory)
        {
            _ipAddress = ipAddress;
            _tcpPort = port;
            _clientList = new List<Client>();
            _workerScheduler = workerScheduler;
            _psEventHandlerFactory = psEventHandlerFactory;
        }

        /// <summary>
        /// calling this will start listen to the port
        /// and if the all client to start
        /// </summary>
        public void StartServingAllClients()
        {
            //TODO: do it the right way using both ip and port to define a connection
            //_serverSocket = new TcpListener(_ipAddress, _tcpPort);
            _serverSocket = new TcpListener(_tcpPort);
            _serverSocket.Start();

            _listenThread = new Thread(new ThreadStart(acceptingConnection));
            _listenThread.Name = "PortListener";
            _log.Info("Start PortListener thread: "+ _listenThread.Name);
            _listenThread.Start();

            _listenThread.Join();
            
            //acceptingConnection();
        }

        private void acceptingConnection()
        {
            bool isActive = true;
            while (isActive)
            {
                //Wait for connection from client
                try
                {
                    _log.Info("Awaiting connection on port " + _tcpPort + " ..");
                    TcpClient clientSocket = _serverSocket.AcceptTcpClient();

                    _log.Info("Accept connection from client on port " + _tcpPort);
                    if (clientSocket != null)
                    {
                        //create client and start serving the client
                        StartServingClient(this,clientSocket);
                    }
                    else
                    {
                        string mgs = "Client should make connection at this point already.";
                        _log.Error(mgs);
                    }
                }
                catch (Exception ex)
                {
                    string msg = "Error accepting connection";
                    _log.Error(msg, ex);
                    isActive = false;
                }
            }
        }

        //calling this will create a new client
        //add this client to the list of client
        //and start serving the client
        private void StartServingClient(ClientManager manager,TcpClient clientSocket)
        {
            string clientName = clientSocket.Client.RemoteEndPoint.ToString();
            _log.Debug("Create new client: "+clientName);
            string threadName = Thread.CurrentThread.Name;
            Client client = new Client(manager,threadName, clientSocket, _workerScheduler, _psEventHandlerFactory);
            
            lock (_clientList)
            {
                _log.Debug("Adding client: " + clientName + " to list");
                _clientList.Add(client);
            }

            _log.Debug("Start serving client: " + clientName);
            client.startServingClient();
 
        }

        //calling this will stop the listener and remove the client from the client list
        private void RemoveClient(Client client)
        {
            _log.Debug("Asked to remove client: "+client.Name + " from list");
            //Only remove client if the client is a valid client 
            if (_clientList.Contains(client))
            {
                _log.Info("Removing client: " + client.Name + " from list");

                _log.Debug("Stop serving client: " + client.Name);
                //stop serving the client
                client.stopServingClient();

                //remove the inactive client
                lock (_clientList)
                {
                    _clientList.Remove(client);
                    _log.Debug("Removed client: " + client.Name + " from list");
                }
            }
            else
            {
                _log.Debug("Could not remove client: " + client.Name + " because it has been removed earlier");
            }
        }

        //calling this will reset all clients managed by this manager
        //only called when the connection with price server need resetting
        private void ResetAllClients(Client requester)
        {
            _log.Debug("Asked to remove all client from list, requested by client: " + requester.Name);
            
            //Only reset all client if the requester is a valid client 
            if (_clientList.Contains(requester))
            {
                lock (_clientList)
                {
                    _log.Info("Remove all client from list, requested by client: " + requester.Name);
                    //Stop serving all client
                    foreach (Client client in _clientList)
                    {
                        _log.Info("Stop serving client : " + client.Name);
                        client.stopServingClient();
                    }

                    _log.Info("Clear client list");
                    //Remove all client 
                    _clientList.Clear();
                }
            }
            else
            {
                _log.Debug("Could not reset all client because requester: " + requester.Name+" is inactive");
            }
        }

        //TODO: need to fix this
        public static byte[] getIPByteArray(string ipString)
        {
            //Split the string
            char[] dot = new char[] {'.'};
            string[] numString = null;
            numString = ipString.Split(dot);
            
            //convert and store in result
            byte[] result = new byte[4];
            for(int i=0;i<4;i++)
            {
                byte iNum = Convert.ToByte(numString[i]);
                result[i] = iNum;
            }

            //return result
            return result;
        }

        //If price server connection is dropped then reset all client (all client is effected)
        public void handlePriceServerReset(Client client)
        {
            ResetAllClients(client);
        }

        //If java server connection is dropped then reset only client in question
        public void handleJavaServerReset(Client client)
        {
            RemoveClient(client);
        }
    }
}
